{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# SAR Single Node on MovieLens (Python, CPU)\n",
    "\n",
    "Simple Algorithm for Recommendation (SAR) is a fast and scalable algorithm for personalized recommendations based on user transaction history. It produces easily explainable and interpretable recommendations and handles \"cold item\" and \"semi-cold user\" scenarios. SAR is a kind of neighborhood based algorithm (as discussed in [Recommender Systems by Aggarwal](https://dl.acm.org/citation.cfm?id=2931100)) which is intended for ranking top items for each user. More details about SAR can be found in the [deep dive notebook](../02_model/sar_deep_dive.ipynb). \n",
    "\n",
    "SAR recommends items that are most ***similar*** to the ones that the user already has an existing ***affinity*** for. Two items are ***similar*** if the users that interacted with one item are also likely to have interacted with the other. A user has an ***affinity*** to an item if they have interacted with it in the past.\n",
    "\n",
    "### Advantages of SAR:\n",
    "- High accuracy for an easy to train and deploy algorithm\n",
    "- Fast training, only requiring simple counting to construct matrices used at prediction time. \n",
    "- Fast scoring, only involving multiplication of the similarity matrix with an affinity vector\n",
    "\n",
    "### Notes to use SAR properly:\n",
    "- Since it does not use item or user features, it can be at a disadvantage against algorithms that do.\n",
    "- It's memory-hungry, requiring the creation of an $mxm$ sparse square matrix (where $m$ is the number of items). This can also be a problem for many matrix factorization algorithms.\n",
    "- SAR favors an implicit rating scenario and it does not predict ratings.\n",
    "\n",
    "This notebook provides an example of how to utilize and evaluate SAR in Python on a CPU.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 训练过程： \n",
    "#### 根据 user-item 关联矩阵计算 Item-item 相似度矩阵\n",
    "### 预测\n",
    "#### user_affinity[user_ids, :].dot(item_similarity) 得分各item得分，清洗已推荐数据，排序输出\n",
    "\n",
    "user-item 关联矩阵 【user_affinity】       \n",
    "item-item 共现矩阵 【item_similarity】   \n",
    "#### 各 item 预测得分\n",
    "socre = user_affinity[user_ids, :].dot(item_similarity) \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "System version: 3.6.10 (default, Mar  5 2020, 10:17:47) [MSC v.1900 64 bit (AMD64)]\n",
      "Numpy  version: 1.16.0\n",
      "Pandas version: 1.0.4\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "import sys\n",
    "import logging\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from common.timer import Timer\n",
    "from common.python_splitters import python_stratified_split\n",
    "from common.python_evaluation import map_at_k, ndcg_at_k, precision_at_k, recall_at_k\n",
    "from sar_model.sar_singlenode import SARSingleNode as SAR\n",
    "\n",
    "print(\"System version: {}\".format(sys.version))\n",
    "print(\"Numpy  version: {}\".format(np.__version__))\n",
    "print(\"Pandas version: {}\".format(pd.__version__))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)-8s %(message)s')\n",
    "\n",
    "# top k items to recommend\n",
    "TOP_K = 10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 读取数据\n",
    "def get_data(data_path):\n",
    "    ratings_title = ['userID','itemID', 'rating', 'timestamp']\n",
    "    data = pd.read_table(os.path.join(data_path, 'ratings.dat'), sep='::',\n",
    "                            header=None, names=ratings_title, engine = 'python')\n",
    "    data['rating'] = data['rating'].astype(np.float32)\n",
    "    # print(data.head())\n",
    "\n",
    "    movies_title = ['MovieID', 'Title', 'Genres']\n",
    "    movies = pd.read_table(os.path.join(data_path, 'movies.dat'),\n",
    "                           sep='::', header=None, names=movies_title, engine='python')\n",
    "\n",
    "    return data, movies\n",
    "\n",
    "\n",
    "# 打印测试结果\n",
    "def print_test(y_true, y_pre, item_map, top_k):\n",
    "    \"\"\"\n",
    "    :param y_true:\n",
    "    :param y_pre:\n",
    "    :param item_map:\n",
    "    :param top_k:\n",
    "    :return:\n",
    "    \"\"\"\n",
    "    y_true = y_true.sort_values(by=['rating'], ascending=False).copy()\n",
    "\n",
    "    eval_map = map_at_k(y_true, y_pre, col_user='userID', col_item='itemID', col_rating='rating', k=top_k)\n",
    "    eval_ndcg = ndcg_at_k(y_true, y_pre, col_user='userID', col_item='itemID', col_rating='rating', k=top_k)\n",
    "    eval_precision = precision_at_k(y_true, y_pre, col_user='userID', col_item='itemID', col_rating='rating', k=top_k)\n",
    "    eval_recall = recall_at_k(y_true, y_pre, col_user='userID', col_item='itemID', col_rating='rating', k=top_k)\n",
    "\n",
    "    print('map_at_k:        {:.3f}'.format(eval_map))\n",
    "    print('ndcg_at_k:       {:.3f}'.format(eval_ndcg))\n",
    "    print('precision_at_k:  {:.3f}'.format(eval_precision))\n",
    "    print('recall_at_k:     {:.3f}\\n'.format(eval_recall))\n",
    "\n",
    "    # 真实值 Item 字典\n",
    "    y_true_item = {v: i for i, v in enumerate(y_true['itemID'])}\n",
    "\n",
    "    for i in range(len(y_true)):\n",
    "        item_i = y_true['itemID'].iloc[i]\n",
    "        rating_i = y_true['rating'].iloc[i]\n",
    "\n",
    "        print('{:3s} {:5d}  {:.3f}  {}      {}'.format('', item_i, rating_i,\n",
    "                                                       item_map.loc[[item_i], ['Title']].values[0][0],\n",
    "                                                       item_map.loc[[item_i], ['Genres']].values[0][0]))\n",
    "\n",
    "    print('- '*20, 'Predict', ' -'*20)\n",
    "    for i in range(len(y_pre)):\n",
    "        item_i = y_pre['itemID'].iloc[i]\n",
    "        rating_i = y_pre['prediction'].iloc[i]\n",
    "\n",
    "        top_i = str(y_true_item[item_i]) if item_i in y_true_item else ''\n",
    "\n",
    "        print('{:3s} {:5d}  {:.3f}  {}      {}'.format(top_i, item_i, rating_i,\n",
    "                                                       item_map.loc[[item_i], ['Title']].values[0][0],\n",
    "                                                       item_map.loc[[item_i], ['Genres']].values[0][0]))\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "TrainData: (750121, 4)\n",
      "    userID  itemID  rating  timestamp\n",
      "19       1    2797     4.0  978302039\n",
      "41       1    1961     5.0  978301590\n",
      "47       1    1207     4.0  978300719\n",
      "12       1    2398     4.0  978302281\n",
      "43       1    2692     4.0  978301570\n",
      "TestData : (250088, 4)\n",
      "    userID  itemID  rating  timestamp\n",
      "23       1     527     5.0  978824195\n",
      "48       1    2028     5.0  978301619\n",
      "10       1     595     5.0  978824268\n",
      "22       1    1270     5.0  978300055\n",
      "18       1    3105     5.0  978301713\n"
     ]
    }
   ],
   "source": [
    "# 读取数据\n",
    "data, movies_map = get_data(data_path = r'.\\Data\\ml-1m')\n",
    "# data = data[:1000]\n",
    "\n",
    "# 拆分训练集、测试集 分组拆开\n",
    "train, test = python_stratified_split(data, ratio=0.75, col_user='userID', col_item='itemID', seed=42)\n",
    "print('TrainData: {}\\n{}'.format(train.shape, train.head()))\n",
    "print('TestData : {}\\n{}'.format(test.shape, test.head()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2020-10-13 00:24:07,068 INFO     Collecting user affinity matrix\n",
      "2020-10-13 00:24:07,099 INFO     Calculating time-decayed affinities\n",
      "2020-10-13 00:24:07,582 INFO     Creating index columns\n",
      "2020-10-13 00:24:09,164 INFO     Calculating normalization factors\n",
      "2020-10-13 00:24:10,099 INFO     Building user affinity sparse matrix\n",
      "2020-10-13 00:24:10,191 INFO     Calculating item co-occurrence\n",
      "2020-10-13 00:24:12,603 INFO     Calculating item similarity\n",
      "2020-10-13 00:24:12,605 INFO     Using jaccard based similarity\n",
      "2020-10-13 00:24:13,604 INFO     Done training\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Took 6.587020855856472 seconds for training.\n"
     ]
    }
   ],
   "source": [
    "# 创建模型\n",
    "model_0 = SAR(\n",
    "    col_user=\"userID\",\n",
    "    col_item=\"itemID\",\n",
    "    col_rating=\"rating\",\n",
    "    col_timestamp=\"timestamp\",\n",
    "    similarity_type=\"jaccard\",\n",
    "    time_decay_coefficient=30,\n",
    "    timedecay_formula=True,\n",
    "    normalize=True\n",
    ")\n",
    "\n",
    "# ########### 训练\n",
    "with Timer() as train_time:\n",
    "    model_0.fit(train)\n",
    "\n",
    "print(\"Took {} seconds for training.\".format(train_time.interval))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2020-10-13 00:24:13,648 INFO     Calculating recommendation scores\n",
      "2020-10-13 00:24:20,391 INFO     Removing seen items\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Took 7.558403924842281 seconds for prediction.\n"
     ]
    }
   ],
   "source": [
    "# ########### 预测\n",
    "with Timer() as test_time:\n",
    "    y_predict = model_0.recommend_k_items(test, remove_seen=True)\n",
    "\n",
    "print(\"Took {} seconds for prediction.\".format(test_time.interval))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "All testdata eval_map:  0.061\n",
      "map_at_k:        0.415\n",
      "ndcg_at_k:       0.606\n",
      "precision_at_k:  0.400\n",
      "recall_at_k:     0.500\n",
      "\n",
      "      380  5.000  Bad Company (1995)      Action\n",
      "      589  5.000  Silence of the Lambs, The (1991)      Drama|Thriller\n",
      "        6  4.000  Sabrina (1995)      Comedy|Romance\n",
      "      480  4.000  Lassie (1994)      Adventure|Children's\n",
      "     3753  4.000  Wonderland (1999)      Drama\n",
      "     3418  3.000  Dorado, El (1967)      Western\n",
      "     3107  3.000  Talented Mr. Ripley, The (1999)      Drama|Mystery|Thriller\n",
      "      377  3.000  When a Man Loves a Woman (1994)      Drama\n",
      "- - - - - - - - - - - - - - - - - - - -  Predict  - - - - - - - - - - - - - - - - - - - -\n",
      "1     589  3.987  Silence of the Lambs, The (1991)      Drama|Thriller\n",
      "3     480  3.906  Lassie (1994)      Adventure|Children's\n",
      "      780  3.850  An Unforgettable Summer (1994)      Drama\n",
      "7     377  3.819  When a Man Loves a Woman (1994)      Drama\n",
      "     1370  3.724  Mars Attacks! (1996)      Action|Comedy|Sci-Fi|War\n",
      "     1240  3.713  M (1931)      Crime|Film-Noir|Thriller\n",
      "0     380  3.653  Bad Company (1995)      Action\n",
      "     1036  3.631  Looking for Richard (1996)      Documentary|Drama\n",
      "     1527  3.623  Last Time I Committed Suicide, The (1997)      Drama\n",
      "      260  3.614  Ladybird Ladybird (1994)      Drama\n"
     ]
    }
   ],
   "source": [
    "eval_map = map_at_k(test, y_predict, col_user='userID', col_item='itemID', col_rating='rating', k=TOP_K)\n",
    "print('All testdata eval_map:  {:.3f}'.format(eval_map))\n",
    "\n",
    "# 打印测试结果\n",
    "print_test(test[test['userID'] == 7], y_predict[y_predict['userID'] == 7], movies_map, top_k=TOP_K)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "### 子模块分析     --- model.compute_time_decay  \n",
    "按最近时间为基准，  对每个评级应用时间衰减  当前时间为1， 按一个月维度  \n",
    "np.minimum(1.0, np.power(0.5, (max_val - value) / half_life))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建模型\n",
    "model = SAR(\n",
    "    col_user=\"userID\",\n",
    "    col_item=\"itemID\",\n",
    "    col_rating=\"rating\",\n",
    "    col_timestamp=\"timestamp\",\n",
    "    similarity_type=\"jaccard\",\n",
    "    time_decay_coefficient=30,\n",
    "    timedecay_formula=True,\n",
    "    normalize=True\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "temp_df = test[test['userID'] == 1].copy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>userID</th>\n",
       "      <th>itemID</th>\n",
       "      <th>rating</th>\n",
       "      <th>timestamp</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>1</td>\n",
       "      <td>595</td>\n",
       "      <td>5.0</td>\n",
       "      <td>978824268</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>23</th>\n",
       "      <td>1</td>\n",
       "      <td>527</td>\n",
       "      <td>5.0</td>\n",
       "      <td>978824195</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>28</th>\n",
       "      <td>1</td>\n",
       "      <td>1545</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978824139</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>20</th>\n",
       "      <td>1</td>\n",
       "      <td>2321</td>\n",
       "      <td>3.0</td>\n",
       "      <td>978302205</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>52</th>\n",
       "      <td>1</td>\n",
       "      <td>1246</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978302091</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>38</th>\n",
       "      <td>1</td>\n",
       "      <td>2762</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978302091</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>14</th>\n",
       "      <td>1</td>\n",
       "      <td>1035</td>\n",
       "      <td>5.0</td>\n",
       "      <td>978301753</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>42</th>\n",
       "      <td>1</td>\n",
       "      <td>1962</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978301753</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>18</th>\n",
       "      <td>1</td>\n",
       "      <td>3105</td>\n",
       "      <td>5.0</td>\n",
       "      <td>978301713</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>48</th>\n",
       "      <td>1</td>\n",
       "      <td>2028</td>\n",
       "      <td>5.0</td>\n",
       "      <td>978301619</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>51</th>\n",
       "      <td>1</td>\n",
       "      <td>608</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978301398</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>1</td>\n",
       "      <td>2804</td>\n",
       "      <td>5.0</td>\n",
       "      <td>978300719</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>22</th>\n",
       "      <td>1</td>\n",
       "      <td>1270</td>\n",
       "      <td>5.0</td>\n",
       "      <td>978300055</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    userID  itemID  rating  timestamp\n",
       "10       1     595     5.0  978824268\n",
       "23       1     527     5.0  978824195\n",
       "28       1    1545     4.0  978824139\n",
       "20       1    2321     3.0  978302205\n",
       "52       1    1246     4.0  978302091\n",
       "38       1    2762     4.0  978302091\n",
       "14       1    1035     5.0  978301753\n",
       "42       1    1962     4.0  978301753\n",
       "18       1    3105     5.0  978301713\n",
       "48       1    2028     5.0  978301619\n",
       "51       1     608     4.0  978301398\n",
       "7        1    2804     5.0  978300719\n",
       "22       1    1270     5.0  978300055"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "temp_df = temp_df.sort_values(by=['timestamp', 'rating'], ascending=False)\n",
    "temp_df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>userID</th>\n",
       "      <th>itemID</th>\n",
       "      <th>rating</th>\n",
       "      <th>timestamp</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>1</td>\n",
       "      <td>595</td>\n",
       "      <td>5.000000</td>\n",
       "      <td>978824268</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>527</td>\n",
       "      <td>4.999902</td>\n",
       "      <td>978824195</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>1</td>\n",
       "      <td>1545</td>\n",
       "      <td>3.999862</td>\n",
       "      <td>978824139</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>1</td>\n",
       "      <td>2321</td>\n",
       "      <td>2.609095</td>\n",
       "      <td>978302205</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>1</td>\n",
       "      <td>1246</td>\n",
       "      <td>3.478687</td>\n",
       "      <td>978302091</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>1</td>\n",
       "      <td>2762</td>\n",
       "      <td>3.478687</td>\n",
       "      <td>978302091</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>1</td>\n",
       "      <td>1035</td>\n",
       "      <td>4.347966</td>\n",
       "      <td>978301753</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>1</td>\n",
       "      <td>1962</td>\n",
       "      <td>3.478372</td>\n",
       "      <td>978301753</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>1</td>\n",
       "      <td>3105</td>\n",
       "      <td>4.347919</td>\n",
       "      <td>978301713</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>1</td>\n",
       "      <td>2028</td>\n",
       "      <td>4.347810</td>\n",
       "      <td>978301619</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>1</td>\n",
       "      <td>608</td>\n",
       "      <td>3.478042</td>\n",
       "      <td>978301398</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>1</td>\n",
       "      <td>2804</td>\n",
       "      <td>4.346763</td>\n",
       "      <td>978300719</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>1</td>\n",
       "      <td>1270</td>\n",
       "      <td>4.345992</td>\n",
       "      <td>978300055</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    userID  itemID    rating  timestamp\n",
       "1        1     595  5.000000  978824268\n",
       "0        1     527  4.999902  978824195\n",
       "6        1    1545  3.999862  978824139\n",
       "9        1    2321  2.609095  978302205\n",
       "4        1    1246  3.478687  978302091\n",
       "10       1    2762  3.478687  978302091\n",
       "3        1    1035  4.347966  978301753\n",
       "7        1    1962  3.478372  978301753\n",
       "12       1    3105  4.347919  978301713\n",
       "8        1    2028  4.347810  978301619\n",
       "2        1     608  3.478042  978301398\n",
       "11       1    2804  4.346763  978300719\n",
       "5        1    1270  4.345992  978300055"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "temp_df_decay = model.compute_time_decay(df=temp_df.copy(), decay_column='rating')\n",
    "temp_df_decay = temp_df_decay.sort_values(by=['timestamp', 'rating'], ascending=False)\n",
    "temp_df_decay "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5.000  5.000   0\n",
      "5.000  5.000   -73\n",
      "4.000  4.000   -129\n",
      "3.000  2.609   -522063\n",
      "4.000  3.479   -522177\n",
      "4.000  3.479   -522177\n",
      "5.000  4.348   -522515\n",
      "4.000  3.478   -522515\n",
      "5.000  4.348   -522555\n",
      "5.000  4.348   -522649\n",
      "4.000  3.478   -522870\n",
      "5.000  4.347   -523549\n",
      "5.000  4.346   -524213\n"
     ]
    }
   ],
   "source": [
    "for i in range(len(temp_df)):\n",
    "    print('{:.3f}  {:.3f}   {}'.format(temp_df['rating'].iloc[i],  temp_df_decay['rating'].iloc[i],\n",
    "                                       temp_df_decay['timestamp'].iloc[i] - temp_df_decay['timestamp'].iloc[0]))\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "### 子模块分析   \n",
    "user-item 关联矩阵 【user_affinity】       \n",
    "item-item 共现矩阵 【item_similarity】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = SAR(\n",
    "    col_user=\"userID\",\n",
    "    col_item=\"itemID\",\n",
    "    col_rating=\"rating\",\n",
    "    col_timestamp=\"timestamp\",\n",
    "    similarity_type=\"jaccard\",\n",
    "    time_decay_coefficient=30,\n",
    "    timedecay_formula=True,\n",
    "    normalize=True\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>userID</th>\n",
       "      <th>itemID</th>\n",
       "      <th>rating</th>\n",
       "      <th>timestamp</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>18</th>\n",
       "      <td>1</td>\n",
       "      <td>3105</td>\n",
       "      <td>5.0</td>\n",
       "      <td>978301713</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>1</td>\n",
       "      <td>2804</td>\n",
       "      <td>5.0</td>\n",
       "      <td>978300719</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>163</th>\n",
       "      <td>2</td>\n",
       "      <td>3095</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978298517</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>90</th>\n",
       "      <td>2</td>\n",
       "      <td>3255</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978299321</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>82</th>\n",
       "      <td>2</td>\n",
       "      <td>3071</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978299120</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>54</th>\n",
       "      <td>2</td>\n",
       "      <td>3068</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978299000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>105</th>\n",
       "      <td>2</td>\n",
       "      <td>2858</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978298434</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>76</th>\n",
       "      <td>2</td>\n",
       "      <td>3108</td>\n",
       "      <td>3.0</td>\n",
       "      <td>978299712</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>170</th>\n",
       "      <td>2</td>\n",
       "      <td>2943</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978298372</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>124</th>\n",
       "      <td>2</td>\n",
       "      <td>3334</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978298958</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>223</th>\n",
       "      <td>3</td>\n",
       "      <td>3168</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978297570</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>200</th>\n",
       "      <td>3</td>\n",
       "      <td>3619</td>\n",
       "      <td>2.0</td>\n",
       "      <td>978298201</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>202</th>\n",
       "      <td>3</td>\n",
       "      <td>2858</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978297039</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     userID  itemID  rating  timestamp\n",
       "18        1    3105     5.0  978301713\n",
       "7         1    2804     5.0  978300719\n",
       "163       2    3095     4.0  978298517\n",
       "90        2    3255     4.0  978299321\n",
       "82        2    3071     4.0  978299120\n",
       "54        2    3068     4.0  978299000\n",
       "105       2    2858     4.0  978298434\n",
       "76        2    3108     3.0  978299712\n",
       "170       2    2943     4.0  978298372\n",
       "124       2    3334     4.0  978298958\n",
       "223       3    3168     4.0  978297570\n",
       "200       3    3619     2.0  978298201\n",
       "202       3    2858     4.0  978297039"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "temp_df = test[(test['userID'] == 1) | (test['userID'] == 2) | (test['userID'] == 3)].copy()\n",
    "temp_df = temp_df[temp_df['itemID']>2800].copy()\n",
    "\n",
    "temp_df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>userID</th>\n",
       "      <th>itemID</th>\n",
       "      <th>rating</th>\n",
       "      <th>timestamp</th>\n",
       "      <th>_indexed_items</th>\n",
       "      <th>_indexed_users</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>18</th>\n",
       "      <td>1</td>\n",
       "      <td>3105</td>\n",
       "      <td>5.0</td>\n",
       "      <td>978301713</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>1</td>\n",
       "      <td>2804</td>\n",
       "      <td>5.0</td>\n",
       "      <td>978300719</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>163</th>\n",
       "      <td>2</td>\n",
       "      <td>3095</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978298517</td>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>90</th>\n",
       "      <td>2</td>\n",
       "      <td>3255</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978299321</td>\n",
       "      <td>3</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>82</th>\n",
       "      <td>2</td>\n",
       "      <td>3071</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978299120</td>\n",
       "      <td>4</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>54</th>\n",
       "      <td>2</td>\n",
       "      <td>3068</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978299000</td>\n",
       "      <td>5</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>105</th>\n",
       "      <td>2</td>\n",
       "      <td>2858</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978298434</td>\n",
       "      <td>6</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>76</th>\n",
       "      <td>2</td>\n",
       "      <td>3108</td>\n",
       "      <td>3.0</td>\n",
       "      <td>978299712</td>\n",
       "      <td>7</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>170</th>\n",
       "      <td>2</td>\n",
       "      <td>2943</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978298372</td>\n",
       "      <td>8</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>124</th>\n",
       "      <td>2</td>\n",
       "      <td>3334</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978298958</td>\n",
       "      <td>9</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>223</th>\n",
       "      <td>3</td>\n",
       "      <td>3168</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978297570</td>\n",
       "      <td>10</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>200</th>\n",
       "      <td>3</td>\n",
       "      <td>3619</td>\n",
       "      <td>2.0</td>\n",
       "      <td>978298201</td>\n",
       "      <td>11</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>202</th>\n",
       "      <td>3</td>\n",
       "      <td>2858</td>\n",
       "      <td>4.0</td>\n",
       "      <td>978297039</td>\n",
       "      <td>6</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     userID  itemID  rating  timestamp  _indexed_items  _indexed_users\n",
       "18        1    3105     5.0  978301713               0               0\n",
       "7         1    2804     5.0  978300719               1               0\n",
       "163       2    3095     4.0  978298517               2               1\n",
       "90        2    3255     4.0  978299321               3               1\n",
       "82        2    3071     4.0  978299120               4               1\n",
       "54        2    3068     4.0  978299000               5               1\n",
       "105       2    2858     4.0  978298434               6               1\n",
       "76        2    3108     3.0  978299712               7               1\n",
       "170       2    2943     4.0  978298372               8               1\n",
       "124       2    3334     4.0  978298958               9               1\n",
       "223       3    3168     4.0  978297570              10               2\n",
       "200       3    3619     2.0  978298201              11               2\n",
       "202       3    2858     4.0  978297039               6               2"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.set_index(temp_df)\n",
    "\n",
    "# add mapping of user and item ids to indices   添加用户和项id到索引的映射\n",
    "temp_df.loc[:, model.col_item_id] = temp_df['itemID'].apply(lambda item: model.item2index.get(item, np.NaN))\n",
    "temp_df.loc[:, model.col_user_id] = temp_df['userID'].apply(lambda user: model.user2index.get(user, np.NaN))\n",
    "temp_df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "关联矩阵:\n",
      "[[5. 5. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 4. 4. 4. 4. 4. 3. 4. 4. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 4. 0. 0. 0. 4. 2.]]\n"
     ]
    }
   ],
   "source": [
    "# 评分转换成 稀疏矩阵 \n",
    "user_affinity = model.compute_affinity_matrix(df=temp_df, rating_col='rating')\n",
    "\n",
    "print('关联矩阵:')\n",
    "# print(user_affinity)\n",
    "print(user_affinity.toarray()) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Item 共现矩阵:\n",
      "[[1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 1. 1. 1. 1. 1. 1. 1. 1. 0. 0.]\n",
      " [0. 0. 1. 1. 1. 1. 1. 1. 1. 1. 0. 0.]\n",
      " [0. 0. 1. 1. 1. 1. 1. 1. 1. 1. 0. 0.]\n",
      " [0. 0. 1. 1. 1. 1. 1. 1. 1. 1. 0. 0.]\n",
      " [0. 0. 1. 1. 1. 1. 2. 1. 1. 1. 1. 1.]\n",
      " [0. 0. 1. 1. 1. 1. 1. 1. 1. 1. 0. 0.]\n",
      " [0. 0. 1. 1. 1. 1. 1. 1. 1. 1. 0. 0.]\n",
      " [0. 0. 1. 1. 1. 1. 1. 1. 1. 1. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 1. 1.]\n",
      " [0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 1. 1.]]\n"
     ]
    }
   ],
   "source": [
    "# 共现矩阵\n",
    "item_cooccurrence = model.compute_coocurrence_matrix(df=temp_df)\n",
    "print('Item 共现矩阵:')  # itemNo * itemNo\n",
    "print(item_cooccurrence.toarray()) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1., 1., 1., 1., 1., 1., 2., 1., 1., 1., 1., 1.], dtype=float32)"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "item_cooccurrence.diagonal()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1. , 1. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ],\n",
       "       [1. , 1. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ],\n",
       "       [0. , 0. , 1. , 1. , 1. , 1. , 0.5, 1. , 1. , 1. , 0. , 0. ],\n",
       "       [0. , 0. , 1. , 1. , 1. , 1. , 0.5, 1. , 1. , 1. , 0. , 0. ],\n",
       "       [0. , 0. , 1. , 1. , 1. , 1. , 0.5, 1. , 1. , 1. , 0. , 0. ],\n",
       "       [0. , 0. , 1. , 1. , 1. , 1. , 0.5, 1. , 1. , 1. , 0. , 0. ],\n",
       "       [0. , 0. , 0.5, 0.5, 0.5, 0.5, 1. , 0.5, 0.5, 0.5, 0.5, 0.5],\n",
       "       [0. , 0. , 1. , 1. , 1. , 1. , 0.5, 1. , 1. , 1. , 0. , 0. ],\n",
       "       [0. , 0. , 1. , 1. , 1. , 1. , 0.5, 1. , 1. , 1. , 0. , 0. ],\n",
       "       [0. , 0. , 1. , 1. , 1. , 1. , 0.5, 1. , 1. , 1. , 0. , 0. ],\n",
       "       [0. , 0. , 0. , 0. , 0. , 0. , 0.5, 0. , 0. , 0. , 1. , 1. ],\n",
       "       [0. , 0. , 0. , 0. , 0. , 0. , 0.5, 0. , 0. , 0. , 1. , 1. ]],\n",
       "      dtype=float32)"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# JACCARD\n",
    "from common.python_utils import jaccard\n",
    "jaccard(item_cooccurrence)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1. , 1. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ],\n",
       "       [1. , 1. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ],\n",
       "       [0. , 0. , 1. , 1. , 1. , 1. , 0.5, 1. , 1. , 1. , 0. , 0. ],\n",
       "       [0. , 0. , 1. , 1. , 1. , 1. , 0.5, 1. , 1. , 1. , 0. , 0. ],\n",
       "       [0. , 0. , 1. , 1. , 1. , 1. , 0.5, 1. , 1. , 1. , 0. , 0. ],\n",
       "       [0. , 0. , 1. , 1. , 1. , 1. , 0.5, 1. , 1. , 1. , 0. , 0. ],\n",
       "       [0. , 0. , 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],\n",
       "       [0. , 0. , 1. , 1. , 1. , 1. , 0.5, 1. , 1. , 1. , 0. , 0. ],\n",
       "       [0. , 0. , 1. , 1. , 1. , 1. , 0.5, 1. , 1. , 1. , 0. , 0. ],\n",
       "       [0. , 0. , 1. , 1. , 1. , 1. , 0.5, 1. , 1. , 1. , 0. , 0. ],\n",
       "       [0. , 0. , 0. , 0. , 0. , 0. , 0.5, 0. , 0. , 0. , 1. , 1. ],\n",
       "       [0. , 0. , 0. , 0. , 0. , 0. , 0.5, 0. , 0. , 0. , 1. , 1. ]],\n",
       "      dtype=float32)"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# lift\n",
    "from common.python_utils import lift\n",
    "lift(item_cooccurrence)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "# item 相似度\n",
    "item_similarity = item_cooccurrence  #  jaccard(item_cooccurrence)   lift(item_cooccurrence)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "### 预测   \n",
    "user-item 关联矩阵 【user_affinity】       \n",
    "item-item 共现矩阵 【item_similarity】   \n",
    "#### 各 item 预测得分\n",
    "socre = user_affinity[user_ids, :].dot(item_similarity)  \n",
    "#### 排序输出"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[10., 10.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],\n",
       "       [ 0.,  0., 31., 31., 31., 31., 35., 31., 31., 31.,  4.,  4.],\n",
       "       [ 0.,  0.,  4.,  4.,  4.,  4., 14.,  4.,  4.,  4., 10., 10.]],\n",
       "      dtype=float32)"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "user_affinity.dot(item_similarity).toarray()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<6040x3664 sparse matrix of type '<class 'numpy.float64'>'\n",
       "\twith 750121 stored elements in Compressed Sparse Row format>"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#　关联矩阵\n",
    "user_affinity = model_0.user_affinity\n",
    "user_affinity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(3664, 3664)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([[1.0000000e+00, 2.2138292e-01, 1.5555556e-01, ..., 9.1407681e-04,\n",
       "        0.0000000e+00, 0.0000000e+00],\n",
       "       [2.2138292e-01, 1.0000000e+00, 1.5126626e-01, ..., 0.0000000e+00,\n",
       "        0.0000000e+00, 9.9206355e-04],\n",
       "       [1.5555556e-01, 1.5126626e-01, 1.0000000e+00, ..., 0.0000000e+00,\n",
       "        0.0000000e+00, 1.4836795e-03],\n",
       "       ...,\n",
       "       [9.1407681e-04, 0.0000000e+00, 0.0000000e+00, ..., 1.0000000e+00,\n",
       "        0.0000000e+00, 0.0000000e+00],\n",
       "       [0.0000000e+00, 0.0000000e+00, 0.0000000e+00, ..., 0.0000000e+00,\n",
       "        1.0000000e+00, 0.0000000e+00],\n",
       "       [0.0000000e+00, 9.9206355e-04, 1.4836795e-03, ..., 0.0000000e+00,\n",
       "        0.0000000e+00, 1.0000000e+00]], dtype=float32)"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#  共现矩阵\n",
    "item_similarity = model_0.item_similarity\n",
    "print(item_similarity.shape)\n",
    "item_similarity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "user_ids = [7]\n",
    "\n",
    "# 各 item 预测得分\n",
    "socre = user_affinity[user_ids, :].dot(item_similarity)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[6.04869880e-07, 5.44859518e-07, 4.51010958e-07, ...,\n",
       "        5.68849126e-09, 4.18515351e-10, 4.08144539e-09]])"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "socre"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "matrix([[6.04869880e-07, 5.44859518e-07, 4.51010958e-07, ...,\n",
       "         5.68849126e-09, 4.18515351e-10, 4.08144539e-09]])"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "socre += user_affinity[user_ids, :] * -np.inf  # 移除训练集中 item\n",
    "socre"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1704, 457, 318, 2291, 1784, 2890, 1610, 3255, 1573, 380]"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from common.python_utils import get_top_k_scored_items\n",
    "\n",
    "# 排序获取 top_k 个候选 Item\n",
    "top_items, top_scores = get_top_k_scored_items(scores=socre, top_k=10, sort_top_k=True)\n",
    "\n",
    "top_items = [model_0.index2item[item] for item in top_items.flatten()]\n",
    "top_items"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "PY36-tf12",
   "language": "python",
   "name": "py36_b"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
